/*
 * TMF muxer
 * Copyright (c) 2003 The FFmpeg Project
 *
 * This file is part of FFmpeg.
 *
 * FFmpeg is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * FFmpeg is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with FFmpeg; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include "libavutil/intreadwrite.h"
#include "libavutil/dict.h"
#include "libavutil/intfloat.h"
#include "libavutil/avassert.h"
#include "avc.h"
#include "avformat.h"
#include "internal.h"
#include "metadata.h"

#include "tmfile.h"

static const AVCodecTag tmfile_video_codec_ids[] = {
    { AV_CODEC_ID_RAWVIDEO,     TMF_CODEC_ID_YUV420SP },
    { AV_CODEC_ID_H264,    TMF_CODEC_ID_H264 },
    { AV_CODEC_ID_H263,    TMF_CODEC_ID_H263 },
    { AV_CODEC_ID_TIVC,    TMF_CODEC_ID_TIVC },    
    { AV_CODEC_ID_NONE,     0 }
};

static const AVCodecTag tmfile_audio_codec_ids[] = {
    { AV_CODEC_ID_PCM_S16LE,        TMF_CODEC_ID_PCM_S16LE },
	{ AV_CODEC_ID_AAC,        TMF_CODEC_ID_AAC },
    { AV_CODEC_ID_NONE,       0 }
};

typedef struct tmfContext {
    int     reserved;
    int64_t duration_offset;
    int64_t filesize_offset;
    int64_t duration;
    int64_t delay;      ///< first dts delay (needed for AVC & Speex)
} tmfContext;

typedef struct FLVStreamContext {
    int64_t last_ts;    ///< last timestamp for each stream
} FLVStreamContext;

static int tmfile_write_header(AVFormatContext *s)
{
    AVIOContext *pb = s->pb;
    tmfContext *flv = s->priv_data;
    int i;
	int version = 100;
	char flag=0;
	tmf_vcodec_info vcodec;
	tmf_acodec_info acodec;
    double framerate = 0.0;

    for (i = 0; i < s->nb_streams; i++) {
        AVCodecContext *enc = s->streams[i]->codec;
        FLVStreamContext *sc;
        switch (enc->codec_type) {
        case AVMEDIA_TYPE_VIDEO:
			flag |= TMF_FLAG_HASVIDEO;
            if (s->streams[i]->avg_frame_rate.den &&
                s->streams[i]->avg_frame_rate.num) {
                framerate = av_q2d(s->streams[i]->avg_frame_rate);
            } else {
                framerate = 1 / av_q2d(s->streams[i]->codec->time_base);
            }
			av_log(s, AV_LOG_INFO, "enc,codec_id=%x,codec_tag=%d(%x) %.2f\n",enc->codec_id,enc->codec_tag,enc->pix_fmt,framerate);
            if (enc->codec_tag == 0) {
                av_log(s, AV_LOG_ERROR, "Video codec '%s' for stream %d is not compatible with TMF\n",
                       avcodec_get_name(enc->codec_id), i);
                return AVERROR(EINVAL);
            }
			if( enc->codec_tag == TMF_CODEC_ID_YUV420SP )
			{
				switch(enc->pix_fmt)
				{
				case AV_PIX_FMT_NV12:
					vcodec.codec_id = TMF_CODEC_ID_YUV420SP;	
					break;
				case AV_PIX_FMT_YUV420P:
					vcodec.codec_id = TMF_CODEC_ID_YUV420P;
					break;
				case AV_PIX_FMT_YUYV422:
					vcodec.codec_id = TMF_CODEC_ID_YUYV422;
					break;					
				case AV_PIX_FMT_RGB24:
					vcodec.codec_id = TMF_CODEC_ID_RGB24;
					break;
				default:
	                av_log(s, AV_LOG_ERROR, "unsupported pix_fmt %d\n",enc->pix_fmt);
	                return AVERROR(EINVAL);
				}
			}else
			{
				vcodec.codec_id = enc->codec_tag;
				//av_log(s, AV_LOG_ERROR, "enc->codec_tag=%d %d",enc->codec_tag,enc->codec_id);
			}
			vcodec.extradata_size = enc->extradata_size;
			vcodec.extradata = enc->extradata;
			vcodec.width = enc->width;
			vcodec.height = enc->height;
			vcodec.bit_rate = enc->bit_rate;
			vcodec.has_b_frames = enc->has_b_frames;
			vcodec.frame_rate = (int)(framerate*100);
			av_log(s, AV_LOG_ERROR, "streams,pts_wrap_bits=%d(%d/%d)\n",s->streams[i]->pts_wrap_bits,s->streams[i]->time_base.den,s->streams[i]->time_base.num);
            break;
        case AVMEDIA_TYPE_AUDIO:
			flag |= TMF_FLAG_HASAUDIO;
			av_log(s, AV_LOG_INFO, "enc,codec_id=%x,codec_tag=%d(%x) %d\n",enc->codec_id,enc->codec_tag,enc->sample_rate,enc->bits_per_coded_sample);
            if (enc->codec_tag == 0) {
                av_log(s, AV_LOG_ERROR, "Audio codec '%s' for stream %d is not compatible with TMF\n",
                       avcodec_get_name(enc->codec_id), i);
                return AVERROR(EINVAL);
            }
			acodec.codec_id = enc->codec_tag;
			acodec.sample_rate = enc->sample_rate;
			acodec.channels = enc->channels;
			acodec.bits_per_coded_sample = enc->bits_per_coded_sample;
			acodec.extradata_size = enc->extradata_size;
			acodec.extradata = enc->extradata;
            break;
        case AVMEDIA_TYPE_DATA:
            break;
        default:
            av_log(s, AV_LOG_ERROR, "Codec type '%s' for stream %d is not compatible with TMF\n",
                   av_get_media_type_string(enc->codec_type), i);
            return AVERROR(EINVAL);
        }
        avpriv_set_pts_info(s->streams[i], 32, 1, 1000); /* 32 bit pts in ms */

        sc = av_mallocz(sizeof(FLVStreamContext));
        if (!sc)
            return AVERROR(ENOMEM);
        s->streams[i]->priv_data = sc;
        sc->last_ts = -1;
    }

    flv->delay = AV_NOPTS_VALUE;

	avio_write(pb, "TMF", 3);
    avio_w8(pb, flag);
	avio_wb32(pb,version);

    if(flag & TMF_FLAG_HASVIDEO)
    {
		avio_write(pb,(const unsigned char *)&vcodec,sizeof(vcodec));
		if( vcodec.extradata_size > 0 )
			avio_write(pb,vcodec.extradata,vcodec.extradata_size);
	        //ff_isom_write_avcc(pb, extradata,extradata_size);
    }
    if(flag & TMF_FLAG_HASAUDIO)
    {
		avio_write(pb,(const unsigned char *)&acodec,sizeof(acodec));
		if( acodec.extradata_size > 0 )
			avio_write(pb,acodec.extradata,acodec.extradata_size);
    }
    return 0;
}

static int tmfile_write_trailer(AVFormatContext *s)
{
    avio_flush(s->pb);
    return 0;
}

static int tmfile_write_packet(AVFormatContext *s, AVPacket *pkt)
{
    AVIOContext *pb      = s->pb;
    AVCodecContext *enc  = s->streams[pkt->stream_index]->codec;
    int size = pkt->size;
	tmf_frame_info frame;
    switch (enc->codec_type) {
    case AVMEDIA_TYPE_VIDEO:
		frame.type = tmfVideoFrame;
        break;
    case AVMEDIA_TYPE_AUDIO:
		frame.type = tmfAudioFrame;
        break;
//    case AVMEDIA_TYPE_DATA:
//		return 0;
    default:
        return AVERROR(EINVAL);
    }
	if( frame.type != tmfVideoFrame && frame.type != tmfAudioFrame )
	{
		av_log(s, AV_LOG_ERROR,"err,frame type err=%d %lld\n",frame.type,avio_tell(s->pb));
        return AVERROR_EOF;
	}
	frame.key_frame = pkt->flags;
	frame.size = size;
	frame.pts = pkt->pts;
	frame.dts = pkt->dts;
	
	avio_write(pb,(const unsigned char *)&frame,sizeof(frame));
	avio_write(pb,pkt->data,size);
{
	static int dbgcounter = 0;
	if( dbgcounter++ < 10 )
		 av_log(NULL, AV_LOG_ERROR,"tmfile_write_packet size=%d %d dts=%lld,pts=%lld\n",frame.size,frame.key_frame,frame.dts,frame.pts);
}
    return pb->error;
}

AVOutputFormat ff_tmfile_muxer = {
    .name           = "tmf",
    .long_name      = NULL_IF_CONFIG_SMALL("Temobi Streaming Format"),
    .mime_type      = "video/tmf",
    .extensions     = "tmf",
    .priv_data_size = sizeof(tmfContext),
    .audio_codec    = AV_CODEC_ID_PCM_S16LE,
    .video_codec    = AV_CODEC_ID_H264,
    .write_header   = tmfile_write_header,
    .write_packet   = tmfile_write_packet,
    .write_trailer  = tmfile_write_trailer,
    .codec_tag      = (const AVCodecTag* const []) {
                          tmfile_video_codec_ids, tmfile_audio_codec_ids, 0
                      },
    .flags          = AVFMT_GLOBALHEADER                      
};
